﻿using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using NativeCode.Core.Data;
using NativeCode.Core.Extensions;
using NativeCode.Web.Controls.Filters;
using NativeCode.Web.Extensions;

namespace NativeCode.Web.Controls.Renderers
{
	public class QueryFilterBarRenderer<TDataItem> : ControlRenderer
		where TDataItem : new()
	{
		public QueryFilterBarRenderer(HtmlTextWriter writer, QueryFilterBar<TDataItem> filterBar, QbeFilterBarRenderOptions options)
			: base(writer)
		{
			FilterBar = filterBar;
			Options = options;
		}

		protected QueryFilterBar<TDataItem> FilterBar { get; private set; }

		protected QbeFilterBarRenderOptions Options { get; private set; }

		public void RenderFilterBarControl()
		{
			FilterBar.CssNames.Add("filterbar");

			HtmlWriter writer = new HtmlWriter(HtmlTextWriter);

			// DIV
			writer.Tag(HtmlTextWriterTag.Div);
			writer.Attribute(HtmlTextWriterAttribute.Class, FilterBar.CssClass);

			// DIV > DIV
			writer.Tag(HtmlTextWriterTag.Div);
			writer.Attribute(HtmlTextWriterAttribute.Class, "contents");

			// DIV > DIV > DIV
			writer.Tag(HtmlTextWriterTag.Div);
			writer.Attribute(HtmlTextWriterAttribute.Class, "label");
			writer.Write(FilterBar.Label);
			writer.EndTag();

			// DIV > DIV > UL
			writer.Tag(HtmlTextWriterTag.Ul);
			writer.Attribute(HtmlTextWriterAttribute.Class, "options");
			writer.Call(RenderBarItems);
			writer.EndTag();

			// DIV > DIV > DIV
			writer.Tag(HtmlTextWriterTag.Div);
			writer.Attribute(HtmlTextWriterAttribute.Class, "available-filters");
			writer.Call(RenderAvailableFilters, FilterBar.AvailableFilters.DistinctBy(av => av.PropertyName).OrderBy(av => av.PropertyName));
			writer.EndTag();

			writer.EndTag();

			writer.EndTag();

			writer.Close();
		}

		private void RenderBarItems(HtmlWriter writer)
		{
			int count = FilterBar.FilterBarItems.Count;
			for (int i = 0; i < count; i++)
			{
				bool isFirstItem = (i == 0);
				bool isLastItem = (i == count - 1);
				bool canDisplayConjunction = (!isFirstItem && isLastItem) || !isFirstItem;

				QueryFilterBarItem item = FilterBar.FilterBarItems[i];
				// Baby Jesus cries every time you code-format this...
				writer
					.Tag(HtmlTextWriterTag.Li)
						.AttributeIf(HtmlTextWriterAttribute.Class, "option first", isFirstItem)
						.AttributeIf(HtmlTextWriterAttribute.Class, "option last", isLastItem)
						.AttributeIf(HtmlTextWriterAttribute.Class, "option", !(isFirstItem && isLastItem))
						.Attribute("filter-key", item.FilterKey)
							.Tag(HtmlTextWriterTag.A)
								.Attribute(HtmlTextWriterAttribute.Class, "filter")
									.Tag(HtmlTextWriterTag.Div)
										.Attribute(HtmlTextWriterAttribute.Class, "display")
											.TagIf(HtmlTextWriterTag.Span, canDisplayConjunction)
												.Attribute(HtmlTextWriterAttribute.Class, "conjunction")
												.Write(item.FilterConjunction.ToString().ToLower())
											.EndTag()
											.Tag(HtmlTextWriterTag.Span)
												.Attribute(HtmlTextWriterAttribute.Class, "name")
												.WriteFormat("[{0}]", item.FilterPropertyName.ToDisplayText())
											.EndTag()
											.Tag(HtmlTextWriterTag.Span)
												.Attribute(HtmlTextWriterAttribute.Class, "operator")
												.Write(item.FilterOperator.ToSymbol(true))
											.EndTag()
											.Tag(HtmlTextWriterTag.Span)
												.Attribute(HtmlTextWriterAttribute.Class, "value")
												.WriteFormat("&quot;{0}&quot;", item.FilterValueText)
											.EndTag()
									.EndTag()
									.Tag(HtmlTextWriterTag.Div)
										.Attribute(HtmlTextWriterAttribute.Class, "editor")
											.TagIf(HtmlTextWriterTag.Span, canDisplayConjunction)
												.Attribute(HtmlTextWriterAttribute.Class, "conjunction")
												.Call(RenderSelectOptions, item.FilterConjunction.ToDictionary(), item.FilterConjunction.ToString())
											.EndTag()
											.Tag(HtmlTextWriterTag.Span)
												.Attribute(HtmlTextWriterAttribute.Class, "name")
												.Call(RenderSelectOptions, FilterBar.GetAvailableFiltersAsDictionary(), item.FilterPropertyName)
											.EndTag()
											.Tag(HtmlTextWriterTag.Span)
												.Attribute(HtmlTextWriterAttribute.Class, "operator")
												.Call(RenderSelectOptions, item.FilterOperator.ToDictionary(), item.FilterOperator.ToString())
											.EndTag()
											.Tag(HtmlTextWriterTag.Span)
												.Attribute(HtmlTextWriterAttribute.Class, "value")
													.Tag(HtmlTextWriterTag.Input)
														.Attribute(HtmlTextWriterAttribute.Type, "text")
														.AttributeIf(HtmlTextWriterAttribute.Value, item.FilterValueText, !item.FilterValueText.IsNullOrEmpty())
													.EndTag()
											.EndTag()
									.EndTag()
							.EndTag()
					.EndTag();
			}

			bool hasAvailableFilters = (FilterBar.AvailableFilters.Count > 0);
			bool hasBarItems = (count > 0);

			writer
				.TagIf(HtmlTextWriterTag.Li, hasAvailableFilters)
					.Attribute(HtmlTextWriterAttribute.Title, "Add a filter...")
					.Attribute(HtmlTextWriterAttribute.Class, "option add")
						.Tag(HtmlTextWriterTag.A)
							.Attribute(HtmlTextWriterAttribute.Class, "link")
							.Write("add")
						.EndTag()
				.EndTag()
				.TagIf(HtmlTextWriterTag.Li, hasBarItems)
					.Attribute(HtmlTextWriterAttribute.Title, "Remove all filters")
					.Attribute(HtmlTextWriterAttribute.Class, "option reset")
						.Tag(HtmlTextWriterTag.A)
							.Attribute(HtmlTextWriterAttribute.Class, "link")
							.Attribute(HtmlTextWriterAttribute.Href, FilterBar.ToPostBackCaller(QueryFilterBar.PostBackEvents.FiltersCleared, true))
							.Write("reset")
						.EndTag()
				.EndTag();

			writer.Close();
		}

		private void RenderSelectOptions(HtmlWriter writer, IDictionary<string, string> pairs, string selected)
		{
			if (pairs.Any())
			{
				writer.Tag(HtmlTextWriterTag.Select);

				foreach (KeyValuePair<string, string> pair in pairs)
				{
					writer
						.Tag(HtmlTextWriterTag.Option)
						.AttributeIf(HtmlTextWriterAttribute.Selected, "true", (pair.Key == selected))
						.Write(pair.Value)
						.EndTag();
				}

				writer.EndTag();
				writer.Close();
			}
		}

		private void RenderAvailableFilters(HtmlWriter writer, IEnumerable<QueryFilter> filters)
		{
			// Size the columns based on the number of items divided by some optimal number of items per column.
			// We will use 10 items per column as a safe default.
			// TODO: Allow the user to specify the number of columns and items per column.
			if (Options.NumberOfColumns == 0)
			{
				foreach (IEnumerable<QueryFilter> slice in filters.Slice(10))
				{
					writer
						.Tag(HtmlTextWriterTag.Ul)
							.Attribute(HtmlTextWriterAttribute.Class, "filters")
								.Call(RenderAvailableFilterColumn, slice)
						.EndTag();
				}
			}
			else
			{
				writer
					.Tag(HtmlTextWriterTag.Ul)
						.Attribute(HtmlTextWriterAttribute.Class, "filters")
							.Call(RenderAvailableFilterColumn, filters)
					.EndTag();
			}
		}

		private void RenderAvailableFilterColumn(HtmlWriter writer, IEnumerable<QueryFilter> filters)
		{
			foreach (QueryFilter filter in filters)
			{
				writer
					.Tag(HtmlTextWriterTag.Li)
						.Attribute(HtmlTextWriterAttribute.Class, "filter")
						.Attribute(HtmlTextWriterAttribute.Value, filter.Key)
							.Tag(HtmlTextWriterTag.Div)
								.Tag(HtmlTextWriterTag.A)
									.Attribute(HtmlTextWriterAttribute.Class, "link")
									.Tag(HtmlTextWriterTag.Span)
										.Attribute(HtmlTextWriterAttribute.Class, "property-name")
										.Write(filter.PropertyName.ToDisplayText())
									.EndTag()
								.EndTag()
							.EndTag()
					.EndTag();
			}
		}
	}
}